\chapter{异常}

\section{异常}

\subsection{异常（Exception）}

异常就是程序在运行过程中出现的非正常的情况，它可以被捕获并处理，以防止程序崩溃。\\

Exception是一个异常类，发生异常的时候会抛出一个异常对象。如果不处理异常，程序就会被中断。\\

\begin{table}[H]
    \centering
    \setlength{\tabcolsep}{5mm}{
        \begin{tabular}{|l|l|}
            \hline
            \textbf{异常}     & \textbf{描述}                      \\
            \hline
            ArithmeticError   & 数学运算异常                       \\
            \hline
            AttributeError    & 访问或设置不存在的属性时引发的异常 \\
            \hline
            FileNotFoundError & 打开不存在的文件时引发的异常       \\
            \hline
            IndexError        & 下标越界                           \\
            \hline
            KeyError          & 使用不存在的键访问字典时引发的异常 \\
            \hline
            ZeroDivisionError & 除数为零                           \\
            \hline
        \end{tabular}
    }
    \caption{常用异常}
\end{table}

例如当列表访问越界时，会抛出一个IndexError异常；当访问一个不存在的属性时，会抛出一个AttributeError异常。\\

\subsection{捕获异常}

try-except-finally结构可以用于捕获并处理异常，将可能出现异常的代码放在try结构中，将异常处理的代码放在except结构中，finally结构中的代码无论是否出现异常都会执行。\\

当在try结构中出现异常时，程序会跳转到except结构中，执行except结构中的代码。一个异常被处理后，将不再影响程序的执行。\\

\begin{figure}[H]
    \centering
    \includegraphics{img/Chapter9/9-1/1.png}
\end{figure}

\mybox{整除}

\begin{lstlisting}[language=Python]
while True:
    try:
        dividend = int(input("Enter an integer for dividend: "))
        divisor = int(input("Enter an integer for divisor: "))
        quotient = dividend / divisor
        print(dividend, "/", divisor, "=", quotient)
    except ValueError:
        print("Only integers supported.")
    except ZeroDivisionError:
        print("Divisor cannot be 0.")
\end{lstlisting}

\begin{tcolorbox}
    \mybox{运行结果}
    \begin{verbatim}
Enter an integer for dividend: 21
Enter an integer for divisor: 4
21 / 4 = 5.25
Enter an integer for dividend: 5
Enter an integer for divisor: 0
Divisor cannot be 0.
Enter an integer for dividend: 3.6
Only integers supported.
	\end{verbatim}
\end{tcolorbox}

\vspace{0.5cm}

\subsection{raise}

raise关键抛出一个异常，抛出异常后，需要由调用者来处理异常。如果调用者没有处理异常，那么异常就继续向上抛出，直到被处理。\\

\begin{figure}[H]
    \centering
    \includegraphics{img/Chapter9/9-1/2.png}
\end{figure}

\mybox{阶乘}

\begin{lstlisting}[language=Python]
def factorial(n):
    if n < 0:
        raise ValueError(
            "Factorial of negative numbers is not defined."
        )
    
    if n == 0 or n == 1:
        return 1
    return n * factorial(n - 1)

n = int(input("Enter n: "))
try:
    print(n, "! =", factorial(n))
except ValueError as e:
    print(e)
\end{lstlisting}

\begin{tcolorbox}
    \mybox{运行结果}
    \begin{verbatim}
Enter n: -1
Factorial of negative numbers is not defined.
	\end{verbatim}
\end{tcolorbox}

\newpage

\section{自定义异常}

\subsection{自定义异常}

为了满足某些特定的需求，用户可以自定义异常，自定义异常继承于Exception类或其子类。自定义异常的目的是为了提供更具体和有意义的错误处理。\\

\mybox{库存}

\begin{lstlisting}[language=Python]
class OutOfStockException(Exception):
    def __init__(self, message):
        self.message = message

    def __str__(self):
        return self.message
\end{lstlisting}

\begin{lstlisting}[language=Python]
from out_of_stock_exception import OutOfStockException

class Product:
    def __init__(self, name, stock):
        self.name = name
        self.stock = stock

    def purchase(self):
        if self.stock <= 0:
            raise OutOfStockException(self.name + " is out of stock.")
        self.stock -= 1
\end{lstlisting}

\begin{lstlisting}[language=Python]
from product import Product
from out_of_stock_exception import OutOfStockException

def main():
    product = Product("Cheeseburger", 50)

    try:
        for _ in range(60):
            product.purchase()
    except OutOfStockException as e:
        print(e)

if __name__ == "__main__":
    main()
\end{lstlisting}

\begin{tcolorbox}
    \mybox{运行结果}
    \begin{verbatim}
Cheeseburger is out of stock.
	\end{verbatim}
\end{tcolorbox}

\newpage